using System;
using System.Collections.Generic;
using Domain.Entities;
using NHibernate;
using System.Text;

namespace Domain.Services
{
    public static class PrivateMessageService
    {
        public static PrivateMessage GetById(int messageId)
        {
            return NHibernateHelper.GetById<PrivateMessage>(messageId);
        }

        public static IList<PrivateMessage> GetInbox(User user, int page, int messagesPerPage)
        {
            string query = "from PrivateMessage msg where"
                + " msg.IsArchived = false"
                + " and msg.IsTrashedByReceiver = false"
                + " and msg.IsDeletedByReceiver = false"
                + " and msg.Receiver.Id = {0}"
                + " order by msg.DateSent desc";
            query = String.Format(query, user.Id);
            return NHibernateHelper.QueryPaged<PrivateMessage>(query, page, messagesPerPage);
        }

        public static int GetInboxCount(User user)
        {
            string query = "select count(*) from PrivateMessage msg where"
                + " msg.IsArchived = false"
                + " and msg.IsTrashedByReceiver = false"
                + " and msg.IsDeletedByReceiver = false"
                + " and msg.Receiver.Id = {0}";
            query = String.Format(query, user.Id);
            return NHibernateHelper.CountQuery(query);
        }

        public static IList<PrivateMessage> GetOutbox(User user, int page, int messagesPerPage)
        {
            string query = "from PrivateMessage msg where"
                + " msg.IsTrashedBySender = false"
                + " and msg.IsDeletedBySender = false"
                + " and msg.Sender.Id = {0}"
                + " order by msg.DateSent desc";
            query = String.Format(query, user.Id);
            return NHibernateHelper.QueryPaged<PrivateMessage>(query, page, messagesPerPage);
        }

        public static int GetOutboxCount(User user)
        {
            string query = "select count(*) from PrivateMessage msg where"
                + " msg.IsTrashedBySender = false"
                + " and msg.IsDeletedBySender = false"
                + " and msg.Sender.Id = {0}";
            query = String.Format(query, user.Id);
            return NHibernateHelper.CountQuery(query);
        }

        public static IList<PrivateMessage> GetArchive(User user, int page, int messagesPerPage)
        {
            string query = "from PrivateMessage msg where"
                + " msg.IsArchived = true"
                + " and msg.IsTrashedByReceiver = false"
                + " and msg.IsDeletedByReceiver = false"
                + " and msg.Receiver.Id = {0}"
                + " order by msg.DateSent desc";
            query = String.Format(query, user.Id);
            return NHibernateHelper.QueryPaged<PrivateMessage>(query, page, messagesPerPage);
        }

        public static int GetArchiveCount(User user)
        {
            string query = "select count(*) from PrivateMessage msg where"
                + " msg.IsArchived = true"
                + " and msg.IsTrashedBySender = false"
                + " and msg.IsDeletedBySender = false"
                + " and msg.Receiver.Id = {0}";
            query = String.Format(query, user.Id);
            return NHibernateHelper.CountQuery(query);
        }

        public static IList<PrivateMessage> Search(User user, string terms, int page, int messagesPerPage)
        {
            string queryStart = "from PrivateMessage msg where"
                + " ( ( msg.IsTrashedByReceiver = false"
                + " and msg.IsDeletedByReceiver = false"
                + " and msg.Receiver.Id = {0} )"
                + " or"
                + " ( msg.IsTrashedBySender = false"
                + " and msg.IsDeletedBySender = false"
                + " and msg.Sender.Id = {0} ) )"
                + " and ( ";
            StringBuilder queryBuilder = new StringBuilder(queryStart);
            List<string> termsPrepared = SearchService.PrepareTerms(terms);
            bool isFirst = true;
            foreach (string term in termsPrepared)
            {
                if (!isFirst)
                {
                    queryBuilder.Append(" or");
                }
                queryBuilder.Append(" msg.Subject like '");
                queryBuilder.Append(term);
                queryBuilder.Append("' or msg.Content like '");
                queryBuilder.Append(term);
                queryBuilder.Append("'");
                isFirst = false;
            }
            queryBuilder.Append(") order by msg.DateSent desc");
            string query = String.Format(queryBuilder.ToString(), user.Id);
            return NHibernateHelper.QueryPaged<PrivateMessage>(query, page, messagesPerPage);
        }

        public static int GetSearchCount(User user, string terms)
        {
            string queryStart = "select count(*) from PrivateMessage msg where"
                + " ( ( msg.IsTrashedByReceiver = false"
                + " and msg.IsDeletedByReceiver = false"
                + " and msg.Receiver.Id = {0} )"
                + " or"
                + " ( msg.IsTrashedBySender = false"
                + " and msg.IsDeletedBySender = false"
                + " and msg.Sender.Id = {0} ) )"
                + " and ( ";
            StringBuilder queryBuilder = new StringBuilder(queryStart);
            List<string> termsPrepared = SearchService.PrepareTerms(terms);
            bool isFirst = true;
            foreach (string term in termsPrepared)
            {
                if (!isFirst)
                {
                    queryBuilder.Append(" or");
                }
                queryBuilder.Append(" msg.Subject like '");
                queryBuilder.Append(term);
                queryBuilder.Append("' or msg.Content like '");
                queryBuilder.Append(term);
                queryBuilder.Append("'");
                isFirst = false;
            }
            queryBuilder.Append(")");
            string query = String.Format(queryBuilder.ToString(), user.Id);
            return NHibernateHelper.CountQuery(query);
        }

        public static IList<PrivateMessage> GetRecycleBin(User user, int page, int messagesPerPage)
        {
            string query = "from PrivateMessage msg where"
                + " ( msg.IsTrashedByReceiver = true"
                + " and msg.IsDeletedByReceiver = false"
                + " and msg.Receiver.Id = {0} )"
                + " or"
                + " ( msg.IsTrashedBySender = true"
                + " and msg.IsDeletedBySender = false"
                + " and msg.Sender.Id = {0} )"
                + " order by msg.DateSent desc";
            query = String.Format(query, user.Id);
            return NHibernateHelper.QueryPaged<PrivateMessage>(query, page, messagesPerPage);
        }

        public static int GetRecycleBinCount(User user)
        {
            string query = "select count(*) from PrivateMessage msg where"
                + " ( msg.IsTrashedByReceiver = true"
                + " and msg.IsDeletedByReceiver = false"
                + " and msg.Receiver.Id = {0} )"
                + " or"
                + " ( msg.IsTrashedBySender = true"
                + " and msg.IsDeletedBySender = false"
                + " and msg.Sender.Id = {0} )";
            query = String.Format(query, user.Id);
            return NHibernateHelper.CountQuery(query);
        }

        public static void Trash(User user, PrivateMessage message)
        {
            ISession session = NHibernateHelper.Instance.CurrentSession;
            using (ITransaction transaction = session.BeginTransaction())
            {
                if (user == message.Sender)
                {
                    message.IsTrashedBySender = true;
                }
                if (user == message.Receiver)
                {
                    message.IsTrashedByReceiver = true;
                }

                session.SaveOrUpdate(message);
                transaction.Commit();
            }
        }

        public static void Restore(User user, PrivateMessage message)
        {
            ISession session = NHibernateHelper.Instance.CurrentSession;
            using (ITransaction transaction = session.BeginTransaction())
            {
                RestoreMessage(user, message);

                session.SaveOrUpdate(message);
                transaction.Commit();
            }
        }

        private static void RestoreMessage(User user, PrivateMessage message)
        {
            if (message.Sender == user)
            {
                message.IsTrashedBySender = false;
            }
            if (message.Receiver == user)
            {
                message.IsTrashedByReceiver = false;
            }
        }

        public static void RestoreAll(User user)
        {
            ISession session = NHibernateHelper.Instance.CurrentSession;
            using (ITransaction transaction = session.BeginTransaction())
            {
                IList<PrivateMessage> messages = GetAllRecycleBinMessages(user);
                foreach (PrivateMessage message in messages)
                {
                    RestoreMessage(user, message);
                    session.SaveOrUpdate(message);
                }

                transaction.Commit();
            }
        }

        public static void EmptyRecycleBin(User user)
        {
            ISession session = NHibernateHelper.Instance.CurrentSession;
            using (ITransaction transaction = session.BeginTransaction())
            {
                IList<PrivateMessage> messages = GetAllRecycleBinMessages(user);
                foreach (PrivateMessage message in messages)
                {
                    if (message.Sender == user)
                    {
                        message.IsDeletedBySender = true;
                    }
                    if (message.Receiver == user)
                    {
                        message.IsDeletedByReceiver = true;
                    }
                    if (message.IsDeletedByReceiver && message.IsDeletedBySender)
                    {
                        session.Delete(message);
                    }
                    else
                    {
                        session.SaveOrUpdate(message);
                    }
                }

                transaction.Commit();
            }
        }

        private static IList<PrivateMessage> GetAllRecycleBinMessages(User user)
        {
            string query = "from PrivateMessage msg where"
                    + " ( msg.IsTrashedByReceiver = true"
                    + " and msg.IsDeletedByReceiver = false"
                    + " and msg.Receiver.Id = {0} )"
                    + " or"
                    + " ( msg.IsTrashedBySender = true"
                    + " and msg.IsDeletedBySender = false"
                    + " and msg.Sender.Id = {0} )";
            query = String.Format(query, user.Id);
            return NHibernateHelper.ListQuery<PrivateMessage>(query);
        }

        public static PrivateMessage Send(User sender, User receiver, string subject, string content)
        {
            ISession session = NHibernateHelper.Instance.CurrentSession;
            using (ITransaction transaction = session.BeginTransaction())
            {
                PrivateMessage message = new PrivateMessage();
                message.Sender = sender;
                message.Receiver = receiver;
                message.Subject = subject;
                message.Content = content;
                message.DateSent = DateTime.Now;
                message.IsRead = false;
                message.IsTrashedBySender = false;
                message.IsTrashedByReceiver = false;
                message.IsDeletedBySender = false;
                message.IsDeletedByReceiver = false;

                session.SaveOrUpdate(message);
                transaction.Commit();
                return message;
            }
        }

        public static void MarkUnread(PrivateMessage message)
        {
            ISession session = NHibernateHelper.Instance.CurrentSession;
            using (ITransaction transaction = session.BeginTransaction())
            {
                message.IsRead = false;

                session.SaveOrUpdate(message);
                transaction.Commit();
            }
        }

        public static void MarkRead(PrivateMessage message)
        {
            ISession session = NHibernateHelper.Instance.CurrentSession;
            using (ITransaction transaction = session.BeginTransaction())
            {
                message.IsRead = true;

                session.SaveOrUpdate(message);
                transaction.Commit();
            }
        }

        public static void Archive(PrivateMessage message)
        {
            ISession session = NHibernateHelper.Instance.CurrentSession;
            using (ITransaction transaction = session.BeginTransaction())
            {
                message.IsArchived = true;

                session.SaveOrUpdate(message);
                transaction.Commit();
            }
        }
    }
}